package com.AdvancedDemos.LearnVariousAPI.Object_Objects;

import java.util.Arrays;
import java.util.Objects;

public class LearnObject {

    public void test() {
        try {
            this.clone();//clone函数在Obejct类中是Protected修饰的,在Obejct的同包类,以及Obejct的子类中是可以调用的,尽管LearnObject类自己是Object的子类,但是注意是子类调用自己类中继承的clone函数,而不能调用自己同包兄弟类的clone,即不可以stu1.clone(),需要在Stu类中重写clone函数,后Stu同包类可以调用Stu中重写的clone函数
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Stu stu1 = new Stu("张三", 25, new double[]{1.1, 2.2});
        System.out.println(stu1.toString());//sout某个对象时,会默认自动调用toString方法
        // Stu{name='张三', id=25}


        Stu stu2 = new Stu("张三", 25, new double[]{1.1, 2.2});
        System.out.println(stu1.equals(stu2));
        System.out.println(stu1 == stu2);

        Stu stu3 = (Stu) stu1.clone();
        if (stu3 != null) {
            System.out.println("浅克隆:\n" + stu3);
            System.out.println(stu3.id);
            System.out.println(stu3.name.toString().hashCode());
            System.out.println(Arrays.toString(stu3.scores));
        }

        Stu stu4 = (Stu) stu1.clone1();
        if (stu4 != null) {
            System.out.println("深克隆:\n" + stu4);
            System.out.println(stu4.id);
            System.out.println(stu4.name.toString().hashCode());
            System.out.println(Arrays.toString(stu4.scores));
        }


    }
}

class Stu implements Cloneable/*这是一个"标记接口",接口中内容为空,实现标记接口表示具有某种能力*/ {
    String name;
    int id;

    double[] scores;

    public Stu(String name, int id, double[] scores) {
        this.name = name;
        this.id = id;
        this.scores = scores;
    }

    public double[] getScores() {
        return scores;
    }

    public void setScores(double[] scores) {
        this.scores = scores;
    }

    /*
        Object类中的equals()方法内部是使用s1==s2比较两个对象的地址是否相同,但我们往往需要比较的是内容,所以需要重写函数
         */
    @Override
    public boolean equals(Object o) {
        //判断两个对象地址是否一样
        if (this == o) return true;
        //o是空,或者比较者的类型与被比较者的类型不一样,直接返回false
        //getClass()也是Object类中的函数  Student.class != Pig.class
        if (o == null || this.getClass() != o.getClass()) return false;
        //开始比较内容
        Stu stu = (Stu) o;
        return this.id == stu.id && Objects.equals(name, stu.name);
    }

    /**
     * 重写Object类中的toString方法
     *
     * @return String
     */
//    @Override
//    public String toString() {
//        return "Stu{" +
//                "name='" + name + '\'' +
//                ", id=" + id +
//                ", scores=" + Arrays.toString(scores) +
//                '}';
//    }

    /**
     * 重写Object类中的clone函数,对象克隆,当某个对象调用这方法时,返回一个一模一样的新对象,函数体中直接调用了父类的clone函数,返回调用结果,需要配合Cloneable接口才能调用clone函数
     *
     * @return Object
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }


    /**
     * 浅克隆与深克隆
     * <p>
     * 重写Object自动生成的是浅克隆:基础类型直接克隆赋值,而引用类型(包含String)克隆的只是地址,即把把引用变量指向原来的地址,并没有真正新开空间
     * <p>
     * 需要手动改成深克隆:对象中的基础类型数据直接克隆赋值,对象中的字符串常量因其不可变特性还是克隆地址,其他引用类型变量不是拷贝地址,而是真的新开空间new对象在赋值
     *
     * @return Object
     * @throws CloneNotSupportedException
     */
    protected Object clone1() throws CloneNotSupportedException {
        Stu s = (Stu) super.clone();
        s.scores = s.scores.clone();//数组支持clone()方法,会开空间返回一个新的数组
        return s;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Stu() {
    }

    public Stu(String name, int id) {
        this.name = name;
        this.id = id;
    }
}